#pragma once
#include "stdafx.h"
#include "Context_model.h"

Context_model :: Context_model(int length, Context_model *prev, int *context, Link *pred)
{
	size_model = length;
	pred_model = prev;
	first_link = new Link (length, context, pred);
}

Link * Context_model ::Encode_sym(int sym, int *context)
{
	if (size_model == 0)
		return first_link;
	Link *cur = new Link (size_model, context);
	Link *next = first_link;
	while (next != NULL) 
	{
		int a = cur->comparison(next);
		if (a == -1)
			next = next->left;
		else if (a == 1)
			next = next->right;
		else if (a == 0)
		{
			delete cur;
			return next;
		}
	}
	delete cur;
	return NULL;
}

Link * Context_model :: Decode(int *context)
{
	if (size_model == 0)
		return first_link;
	Link *cur = new Link (size_model, context);
	Link *next = first_link;
	while (next != NULL) 
	{
		int a = cur->comparison(next);
		if (a == -1)
			next = next->left;
		else if (a == 1)
			next = next->right;
		else if (a == 0)
		{
			delete cur;
			return next;
		}
	}
	delete cur;
	return NULL;
}

void Context_model :: SingleRightRotate(Link *&cur)
{
	Link *leftson = cur->left;
	cur->balance = 0;
	leftson->balance = 0;
	cur->left = leftson->right;
	leftson->right = cur;
	cur = leftson;
}

void Context_model :: SingleLeftRotate(Link *&cur)
{
	Link *rightson = cur->right;
	cur->balance = 0;
	rightson->balance = 0;
	cur->right = rightson->left;
	rightson->left = cur;
	cur = rightson;
}

void Context_model :: DoubleRightRotate(Link* &cur)
{
	Link *leftson, *nepfew;
	leftson = cur->left;
	nepfew = leftson->right;

	if (nepfew->balance == 1)
	{
		cur->balance = 0;
		leftson->balance = -1;
	}
	else if (nepfew->balance == -1)
	{
		cur->balance = 1;
		leftson->balance = 0;
	}
	else
	{
		cur->balance = 0;
		leftson->balance = 0;
	}
	nepfew->balance = 0;
	leftson->right = nepfew->left;
	nepfew->left = leftson;
	cur->left = nepfew->right;
	nepfew->right = cur;
	cur = nepfew;
}

void Context_model :: DoubleLeftRotate(Link* &cur)
{
	Link *rightson, *nepfew;
	rightson = cur->right;
	nepfew = rightson->left;

	if (nepfew->balance == 1)
	{
		cur->balance = -1;
		rightson->balance = 0;
	}
	else if (nepfew->balance == -1)
	{
		cur->balance = 0;
		rightson->balance = 1;
	}
	else 
	{
		cur->balance = 0;
		rightson->balance = 0;
	}
	nepfew->balance = 0;
	rightson->left = nepfew->right;
	cur->right = nepfew->left;
	nepfew->left = cur;
	nepfew->right = rightson;
	cur = nepfew;
}

void Context_model :: UpdateRightTree(Link *&cur, int &changedbalance)
{
	Link *rightson = cur->right;
	if (rightson->balance == 1)
	{
		SingleLeftRotate(cur);
		changedbalance = 0;
	}
	else if (rightson->balance == -1)
	{
		DoubleLeftRotate(cur);
		changedbalance = 0;
	}
}

void Context_model :: UpdateLeftTree(Link *&cur, int &changedbalance)
{
	Link *leftson = cur->left;
	if (leftson->balance == -1)
	{
		SingleRightRotate(cur);
		changedbalance = 0;
	}
	else if (leftson->balance == 1)
	{
		DoubleRightRotate(cur);
		changedbalance = 0;
	}
}

void Context_model :: Insert(Link* &root, Link *cur, int &changedbalance)
{
	if (root == NULL)
	{
		root = cur;
		changedbalance = 1;
	}
	else if (cur->comparison(root) == -1)
	{
		Insert (root->left, cur, changedbalance);
		if (changedbalance)
		{
			if (root->balance == -1)
				UpdateLeftTree(root, changedbalance);
			else if (root->balance == 0)
			{
				root->balance = -1;
				changedbalance = 1;
			}
			else
			{
				root->balance = 0;
				changedbalance = 0;
			}
		}
		else
			changedbalance = 0;
	}
	else if (cur->comparison(root) == 1)
	{
		Insert (root->right, cur, changedbalance);
		if (changedbalance)
		{
			if (root->balance == 1)
				UpdateRightTree(root, changedbalance);
			else if (root->balance == 0)
			{
				root->balance = 1;
				changedbalance = 1;
			}
			else
			{
				root->balance = 0;
				changedbalance = 0;
			}
		}
		else
		{
			changedbalance = 0;
		}
	}
}

Link * Context_model :: Update (int sym, int *context, Link *match)
{
	int changedbalance = 0;
	if (match->GetLength() == size_model)
	{
		match->Update(sym);
		return NULL;
	}
	Link *next = new Link(size_model, context, match);
	Insert (first_link, next, changedbalance);
	return next;
	
}

Context_model * Context_model :: GetNext()
{
	return pred_model;
}

int Context_model :: GetSize()
{
	return size_model;
}

void DelLink (Link * cur)
{
	if (cur->left != NULL)
		DelLink (cur->left);
	if (cur->right != NULL)
		DelLink(cur->right);
	if ((cur->left == NULL) && (cur->right == NULL))
	{
		delete cur;
		cur = NULL;
	}
}

Context_model :: ~Context_model()
{
	DelLink(first_link);
}